package com.google.code.ptrends.catparsers;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.google.code.ptrends.catparsers.utils.IntPair;
import com.google.code.ptrends.catparsers.utils.ModelManufacturerPair;
import com.google.code.ptrends.common.entities.Category;
import com.google.code.ptrends.common.entities.ItemClass;
import com.google.code.ptrends.common.entities.Manufacturer;
import com.google.code.ptrends.common.entities.Property;
import com.google.code.ptrends.common.entities.Session;
import com.google.code.ptrends.entities.ETLItem;
import com.google.code.ptrends.excel.ExcelReader;
import com.google.code.ptrends.excel.entities.Cell;
import com.google.code.ptrends.excel.entities.Row;
import com.google.code.ptrends.excel.entities.Worksheet;

public abstract class AbstractCategoryParser implements ETLItemFactory {

    private final ExcelReader reader;
    private final Worksheet worksheet;
    private final Session session;
    private static final int DESCR_COLUMN_INDEX = 1;
    private static final int PRICE_COLUMN_INDEX = 4;

    public AbstractCategoryParser(final ExcelReader reader, final Worksheet worksheet,
	    final Session session) {
	if (reader == null)
	    throw new IllegalArgumentException("Illegal null-pointer reader");

	if (worksheet == null)
	    throw new IllegalArgumentException("Illegal null-pointer worksheet");

	if (session == null)
	    throw new IllegalArgumentException("Illegal null-reference session");

	this.reader = reader;
	this.worksheet = worksheet;
	this.session = session;
    }

    @Override
    public ETLItem[] parse(Category category, int supplierID, IntPair categoryPositions) {

	List<ETLItem> result = new ArrayList<ETLItem>();

	int spos = categoryPositions.getA();
	int endpos = categoryPositions.getB();

	HashMap<ModelManufacturerPair, ETLItem> checkMap = new HashMap<ModelManufacturerPair, ETLItem>();

	// Loop through each row in category
	for (int i = spos; i <= endpos; ++i) {
	    try {
		Cell descriptionCell = getDescription(i);
		Cell priceUSDCell = getPriceUSD(i);
		String description = descriptionCell.getValue();
		String price = priceUSDCell.getValue();

		ETLItem item = createItem(description, price);
		if (item != null) {
		    // item.setSession(session);
		    if (item.getItemClass() == null) {
			ItemClass generalClass = new ItemClass();
			generalClass.setName("General");
			item.setItemClass(generalClass);
		    }
		    ModelManufacturerPair mmi = new ModelManufacturerPair(item.getItemModel(), item
			    .getManufacturer().getName());
		    if (!checkMap.containsKey(mmi))
			checkMap.put(mmi, item);
		} else {
		    notifyConstructionError(i, description, getClass());
		}
	    } catch (Exception e) {
		LOG.error(String.format("Error while constructing ETLItem. Row=[%s]", i), e);
	    }
	}
	result.addAll(checkMap.values());
	checkMap.clear();
	return (ETLItem[]) result.toArray(new ETLItem[result.size()]);
    }

    /**
     * Method shouldn't throw any exceptions. Return null if {@link ETLItem} 
     * can't be constructed using given description/priceUSD
     * 
     * @param description
     *            Detailed description of ETLItem
     * @param priceUSD
     *            Price of item in USD
     * @return {@link ETLItem} if it can be constructed from given description
     *         string and price or null otherwise
     */
    protected abstract ETLItem createItem(String description, String priceUSD);

    protected ETLItem buildItem(Category category, String manufacturer, String itemModel,
	    float priceUSD, Date date) {
	ETLItem item = new ETLItem();
	item.setPriceDate(date);
	item.setCategory(category);
	item.setPrice(priceUSD);
	item.setItemModel(itemModel);

	Manufacturer mf = new Manufacturer();
	mf.setName(manufacturer);
	item.setManufacturer(mf);
	item.setSupplierID(session.getSupplierID());
	return item;
    }

    protected Property buildProperty(String name, String value) {
	Property p = new Property();
	p.setName(name);
	p.setValue(value);
	return p;
    }

    private Cell getDescription(int rowIndex) {
	Row row = getCurrentSheetRow(rowIndex);
	return row.getCellAt(DESCR_COLUMN_INDEX);
    }

    private Cell getPriceUSD(int rowIndex) {
	Row row = getCurrentSheetRow(rowIndex);
	return row.getCellAt(PRICE_COLUMN_INDEX);
    }

    private Row getCurrentSheetRow(int rowIndex) {
	return reader.getRow(worksheet, rowIndex);
    }

    private void notifyConstructionError(int rowIndex, String description,
	    @SuppressWarnings("rawtypes") Class parserType) {
	LOG.error(String.format(
		"Error while processing item. Row=[%s], ParserType=[%s], Description=[%s]",
		rowIndex, parserType.getName(), description));
    }

    private static final Logger LOG = Logger.getLogger(AbstractCategoryParser.class);

}
